<?php


namespace app\common\lib\apns;

use Pushok\AuthProvider\Certificate;
use Pushok\Client;
use Pushok\Notification;
use Pushok\Payload;
use Pushok\Payload\Alert;
use think\Log;

class ApnsPush
{

    protected $conf_apns = null;
    protected $data = null;

    /**
     * 发送数据客户端
     * @var \Pushok\Client|null
     */
    protected $client = null;
    protected $payload = null;

    public function __construct($conf_apns = null)
    {
        $conf_apns = $conf_apns ?: config('push.apns');
        $options = [
            'app_bundle_id' => $conf_apns['app_bundle_id'], // The bundle ID for app obtained from Apple developer account
            'certificate_path' => __DIR__ . '/' . $conf_apns['certificate_path'], // Path to private key
            'certificate_secret' => $conf_apns['certificate_secret'] ?? null, // Private key secret
        ];
        $authProvider = Certificate::create($options);
        $this->client = new Client($authProvider, (bool)$conf_apns['apns_production']);
        $this->conf_apns = $conf_apns;

    }

    /**
     * 设置发送数据
     * @param array $data
     * [
     * 'plantform' => $pushType == 1 ? 1 : 3, // 1 => all  2 => IOS 3 => android
     * 'pushType' => $pushType,
     * 'regid' => [$userRegid],
     * 'title' => $params['title'], // 推送标题
     * 'sub_title' => $params['title'], // 推送子标题
     * 'content' => $params['content'], // 推送内容
     * 'extras' => $extras, // 扩展字段
     * 'informType' => 0, //通知栏样式 0默认 1大文本 2收件箱 3大图片
     * 'bigText' => '', // 大文本内容
     * 'bigPicPath' => '', // 大图片路径
     * 'timeToLive' => 86400, // 默认86400
     * 'is_jpush' => (int)$params['is_jpush'], //是否是极光推送（1：是；0：自研推送apns）
     * 'device_tokens' => $userRegidData['device_token'],
     * ]
     * @throws \Pushok\InvalidPayloadException
     */
    public function setData($data = [])
    {
//        $ios_notification = [
//            'sound' => 'sound.caf',         // 通知提示音
//            'badge' => '+1',                // 应用角标数字
//            'content-available' => true,    // 推送唤醒
//            'mutable-content' => true,      // 通知扩展
//            'category' => 'zhunle',         // ios8支持
//            "extras" => $data['extras']     //扩展字段
//        ];
        $this->data = $data;
        $extra = $data['extras'];

        $alert = Alert::create();
        if (isset($data['title']))
        {
            $alert = $alert->setTitle($data['title']);
        } else
        {
            $alert = $alert->setTitle('准了');
        }
        if (isset($data['sub_title']))
        {
            $alert = $alert->setSubtitle($data['sub_title']);
        }
        if (isset($data['content']))
        {
            $alert = $alert->setBody($data['content']);
        } else
        {
            throw new \RuntimeException('content必须要设置', 0);
        }

        $payload = Payload::create()->setAlert($alert);
        $payload->setSound('default');
        $payload->setBadge(1);
        $this->payload = $payload;
        foreach ($extra as $k => $v)
        {
            $this->payload->setCustomValue($k, $v);
        }

        //todo 推送待做，增加图片类型（需要ios端实现，参考：https://www.jianshu.com/p/c0a65526e653）
        //todo action类型 （需要ios端实现，参考：https://www.jianshu.com/p/330bd5a5fcd5）
        //todo 等devicetoken多了以后，再考虑把ios的极光的sdk干掉，自己实现全推功能
        //todo App的badge需要保存一下，现在发送的都是1
        return $this;
    }

    /**
     * apns个推/批量推
     * @param mixed $device_tokens array or string
     * @return array
     */
    public function push($device_tokens): array
    {
        $notifications = [];
        //检测发送的数量
        if (is_array($device_tokens) && empty($device_tokens))
        {
            return [
                'http_code' => 400,
                'message' => '$device_tokens为数组类型，输入为空！',
            ];
        }

        if (is_string($device_tokens) && $device_tokens === '')
        {
            return [
                'http_code' => 400,
                'message' => '$device_tokens为string类型，输入为空！',
            ];
        }
        try
        {

            if (is_array($device_tokens))
            {
                foreach ($device_tokens as $device_token)
                {
                    $notifications[] = new Notification($this->payload, $device_token);
                }
            } else if (is_string($device_tokens))
            {
                $notifications[] = new Notification($this->payload, $device_tokens);
            }

            //设置推送
            $this->client->addNotifications($notifications);
            // 设置通过多路连接发送的并发请求数。默认值:20
            if (isset($this->conf_apns['concurrent_requests']))
            {
                $this->client->setNbConcurrentRequests($this->conf_apns['concurrent_requests']);
            }

            // 设置与APNS服务器的最大并发连接数。默认值:1
            if (isset($this->conf_apns['max_concurrent_connections']))
            {
                $this->client->setMaxConcurrentConnections($this->conf_apns['max_concurrent_connections']);
            }
            //发送推送
            $responses = $this->client->push();

            $result = [
                'count_success' => 0,
                'count_fail' => 0,
            ];
            foreach ($responses as $res)
            {
                $statusCode = $res->getStatusCode();
                if ((int)$statusCode === 200)
                {
                    $result['count_success']++;
                } else if ((int)$statusCode === 0)
                {
                    $result['count_fail']++;
                } else
                {
                    $result['count_fail']++;

                    $detail_fail = [
                        // The device token
                        'device_token' => $res->getDeviceToken(),
                        // A canonical UUID that is the unique ID for the notification. E.g. 123e4567-e89b-12d3-a456-4266554400a0
                        'apns_id' => $res->getApnsId() ?? '',
                        // Status code. E.g. 200 (Success), 410 (The device token is no longer active for the topic.)
                        'status_code' => $res->getStatusCode(),
                        // E.g. The device token is no longer active for the topic.
                        'reason_phrase' => $res->getReasonPhrase(),
                        // E.g. Unregistered
                        'error_reason' => $res->getErrorReason(),
                        // E.g. The device token is inactive for the specified topic.
                        'error_description' => $res->getErrorDescription(),
                        'timestamp' => $res->get410Timestamp(),
                    ];
//                    Log::write('发送apns通知失败：detail_fail=' . json_encode($detail_fail, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . ',push_data=' . json_encode($this->data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), 'log', true);
                }
            }

            $response = [
                'http_code' => 200,
                'message' => 'APNS推送，发送' . count($device_tokens) . '个，成功' . $result['count_success'] . '个，失败' . $result['count_fail'] . '个！',
            ];

//            Log::write('发送apns通知成功：msg=' . $response['message'] . ',responses=' . json_encode($responses), 'log', true);
        }
        catch (\Exception $e)
        {
            $response = [
                'http_code' => 400,
                'message' => $e->getMessage(),
            ];
//            Log::write('发送apns通知失败：msg=' . $e->getMessage() . ',line=' . $e->getLine() . ',file=' . $e->getFile() . ',push_data=' . json_encode($this->data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), 'log', true);
        }

        return $response;
    }


}
